require "base_def"

local short_message = {}

--阿里云短信接口部分
--[[
local t = {"SignatureMethod", "SignatureNonce", "AccessKeyId", "SignatureVersion",
    "Timestamp", "Format", "Action", "Version", "RegionId", "PhoneNumbers",
    "SignName", "TemplateParam", "TemplateCode"
}
--]]
--alySms
--[[
local function SendAlySms()
    local accessSecret = "VsdPCSbEkYqD7d9ttGr8Y18BKneIDY"
    --系统参数
    local msg = {}
    local SignatureMethod = "HMAC-SHA1"
    table.insert(msg, {["key"]="SignatureMethod", ["value"]=SignatureMethod})

    local SignatureNonce = tostring(math.random(1, 1000000))
    table.insert(msg, {["key"]="SignatureNonce", ["value"]=SignatureNonce})

    local AccessKeyId = "LTAI4G3Wf48SyHKdvDfWrtZW"
    table.insert(msg, {["key"]="AccessKeyId", ["value"]=AccessKeyId})

    local SignatureVersion = "1.0"
    table.insert(msg, {["key"]="SignatureVersion", ["value"]=SignatureVersion})

    --格林威治时区：UTC+00:00
    local timeStr = os.date("!*t")  
    local Timestamp = string.format("%s-%s-%sT%s:%s:%sZ", timeStr["year"], timeStr["month"], timeStr["day"],
        timeStr["hour"], timeStr["min"], timeStr["sec"]    
    )
    table.insert(msg, {["key"]="Timestamp", ["value"]=Timestamp})

    local Format = "JSON"
    table.insert(msg, {["key"]="Format", ["value"]=Format})

    --业务参数
    local Action = "SendSms"
    table.insert(msg, {["key"]="Action", ["value"]=Action})

    local Version = "2017-05-25"
    table.insert(msg, {["key"]="Version", ["value"]=Version})

    local RegionId = "cn-hangzhou"
    table.insert(msg, {["key"]="RegionId", ["value"]=RegionId})

    local PhoneNumbers = "13714538724"
    table.insert(msg, {["key"]="PhoneNumbers", ["value"]=PhoneNumbers})

    local SignName = "阿里云短信测试专用"
    table.insert(msg, {["key"]="SignName", ["value"]=SignName})

    local TemplateParam = "{\"customer\":\"test\"}"
    table.insert(msg, {["key"]="TemplateParam", ["value"]=TemplateParam})

    local TemplateCode = "SMS_71390007"
    table.insert(msg, {["key"]="TemplateCode", ["value"]=TemplateCode})

    table.sort(msg, function(a, b)
        return a.key<b.key
    end)
    for k, v in pairs(msg) do
        print(k, v.key,  v.value)
    end
end
--]]

--http_request(host, short_message.url, params, httpc.request, 'GET')
local function http_request(Host, url, params, request, request_method, content, respheader, recvheader)
	--set timeout
    httpc.timeout = short_message.timeout	                    
    if params then
        url = url .. '?' .. params
    end
    recvheader = recvheader or {}
    respheader = respheader or {}
    log.info('http_request:', Host.." "..url)

    local http_req = httpc.get
    if request then
        http_req = function(...) return request(request_method, ...) end
    end

    local status, ret = http_req(Host, url, recvheader, respheader, content)
    log.info("http_request status=", status)
    log.info("http_request ret=", ret)
    if status ~= 200 then
        if status >= 400 then
            log.error('send http_request faild, url is :', Host..url)
            log.error('status:', status.. '  error is ', ret)
            return false, ret
        else
            log.info('send http_request may redirect, status:', status.. '  ret is ', ret)
            return true, ret
        end
    else
        return true, ret
    end
end

local function popEncode(s)
    if not s then
        return ''
    end
    local a = "a+b"
    local s = string.gsub(a, "+", "%20")
    print(s)
end

local function decodeURI(s)
    s = string.gsub(s, '%%(%x%x)', function(h) return string.char(tonumber(h, 16)) end)
    return s
end

local function encodeURI(s)
    if not s then
        return ''
    end
    s = string.gsub(s, "([^%w%.%- ])", function(c) return string.format("%%%02X", string.byte(c)) end)
    return string.gsub(s, " ", "+")
end

--检测发送频率
local function check_rate(channel, phone)                              
    local str = "select args_one from compre_cfg_tbl where belong_fun = 3 and type = 1;"
    --local ok, ret = mysql_tool.query(str)
    local ret = CallMysql(str)
    log.info('check_rate compre_cfg_tbl json:', ret)
    if ret then
        local list = ret[1] and ret[1].args_one
        if list then
            local ok2, info = pcall(cjson.decode, list)
            log.info('check_rate compre_cfg_tbl obj:', info)
            if ok2 and info then
                local t1 = tonumber(info.second)        --60
                local t2 = tonumber(info.minute)        --1
                local t3 = tonumber(info.day)           --1
                local n1 = tonumber(info.s_slip)        --1
                local n2 = tonumber(info.m_slip)        --10
                local n3 = tonumber(info.d_slip)        --10

                local t11 = math.floor(os.time() - t1)      --1分钟之前
                local t22 = math.floor(os.time() - t2 * 60 * 60)    --1小时之前
                local t33 = math.floor(os.time() - t3 * 60 * 60 * 24)   --1天之前

                str = string.format("call pr_get_phone_message_log(%s,%s,%s,%s,%s)", Obj2mstr(channel), Obj2mstr(phone), t11, t22, t33)
                local ret3 = CallMysql(str)
                log.info('check_rate pr_get_phone_message_log:', ret3)
                --判断1分钟不超过n1(1)条，1小时不超过n2条(10条)，1天也不超过n3(10条)
                if ret3 then
                    local info2 = ret3[1] and ret3[1][1]
                    if info2 then
                        local n11, n22, n33 = info2.n1, info2.n2, info2.n3
                        if n11 >= n1 then
                            log.error('cur times:', n11, t1, '秒超过', n1)
                            return -1
                        end
                        if n22 >= n2 then
                            log.error('cur times:', n22, t2, '小时超过', n2)
                            return -1
                        end
                        if n33 >= n3 then
                            log.error('cur times:', n33, t3, '天超过', n3)
                            return -1
                        end
                    end
                end
            end
        end
    end

    return 1
end

--调用一次就生成一个新的验证码
local function gen_code(channel, phone, v_type)                
    local ret = check_rate(channel, phone)
    if ret < 0  then
        return ret
    end

    local code = math.random(100000, 999999)
    if not short_message.phoneCode[phone] then
        short_message.phoneCode[phone] = {}
    end
    --加一个last time防止快超时时再次发生验证码把之前的给置空
    short_message.phoneCode[phone][v_type] = {code=code, last_time=os.time()}
    log.info('gen_code:', code)

    skynet.timeout(100 * short_message.valideTime, function()
        local item = short_message.phoneCode[phone] and short_message.phoneCode[phone][v_type]
        --超时清验证码
        if item and item.last_time then        
            if (os.time() - item.last_time) >= short_message.valideTime then
                short_message.phoneCode[phone][v_type] = nil
                if not next(short_message.phoneCode[phone]) then
                    short_message.phoneCode[phone] = nil
                end
            end
        end
    end)

    return 1, code
end

--短信统计接口
local function message_record(args, raw_content, suc)                     
    local str = string.format("call pr_insert_phone_message_log(%s,%s,%s,%s,%s,%s,%s)",
    Obj2mstr(args.channel), Obj2mstr(args.user_id), Obj2mstr(args.phonenumber), Obj2mstr(args.type), Obj2mstr(raw_content), os.time(), suc and 1 or 0)
    log.info('message_record:', str)
    CallMysql(str)
end

--获取短信模板
local function get_msg_template(v_type)               
    local str = "select args_one from compre_cfg_tbl where belong_fun = 3 and type = 2"
    local ret = CallMysql(str)
    log.info('get_msg_template:', ret)
    if ret then
        local list = ret[1] and ret[1].args_one
        if list then
            local ok2, info = pcall(cjson.decode, list)
            if ok2 and info then
                local field = short_message.msgTemplate[v_type]
                if field then
                    return info[field]
                end
            end
        end
    end
end

--发送
local function send_verify_message(phone, ver_code, v_type)
    local raw_content
    local template_msg = get_msg_template(v_type)
    log.info('get_msg_template:', template_msg)
    if not template_msg then
        log.info('get_msg_template ret failed!')
        raw_content = string.gsub(short_message.rawContent, 'x', ver_code)
    else
        raw_content = string.gsub(template_msg, 'x', ver_code)
    end
    log.info("send_verify_message raw_content=", raw_content)
    local content = encodeURI(raw_content)
    log.info("encodeURI content=", content)

    local snpwd = codec.md5_encode(short_message.sn..short_message.pwd)
    local usnpwd = string.upper(snpwd)
    local params = string.format("sn=%s&pwd=%s&mobile=%s&content=%s&ext=&stime=&rrid=&msgfmt=", short_message.sn, usnpwd, phone, content)
    local index = math.random(1, #short_message.hostList)
    local host = short_message.hostList[index]
    local ok, ret_src = http_request(host, short_message.url, params, httpc.request, 'GET')
    log.info("ret_src=", ret_src)
    if ok and ret_src then
        local function parse_ret(src)
            --由于返回是这样子的只能这样解析了<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<string xmlns=\"http://entinfo.cn/\">xxx</string>
            local str = string.match(src,  ".cn/\">.+<")
            log.info("parse_ret str=", str)
            if str then
                local ret = string.sub(str, 7, -2) or 0
                return tonumber(ret)
            end
        end

        local ret = parse_ret(ret_src)
        log.info("parse_ret ret=", ret)
        if ret and ret > 0 then
            return true, raw_content
        else
            log.error('send_verify_message logic failed, phone: err code:', ret)
        end
    else
        log.error('send_verify_message http failed, phone: err code:', ret_src)
    end
end

--客户端获取验证码
function short_message.SendVerifyMessage(args)      
    --这里有渠道号   
    log.info('send_verify_message:', args)     
    local phone = args.phonenumber
    local v_type = args.type
    if phone then
        --取验证码，并限制频率
        local suc, ver_code = gen_code(args.channel, phone, v_type)
        --1成功-1表示受到频率限制
        if suc == 1 then   
            --调用平台发码
            local okk, raw_content = send_verify_message(phone, ver_code, v_type)   
            --在这里短信统计入库 
            message_record(args, raw_content, okk)
            if okk then 
                return 1 
            end
        else
            return suc
        end
    end

    return     
end

--服务器校验验证码
function short_message.CheckVerifyCode(phone, ver_type, ver_code)    
    --log.info("short_message.phoneCode=", short_message.phoneCode)        
    local item = short_message.phoneCode[phone] and short_message.phoneCode[phone][ver_type]
    log.info('item:', item)
    if phone and ver_code then
        if item and tostring(item.code) == tostring(ver_code) then
            --验证通过之后置空
            item.code = nil                     
            return true, {}
        else
            log.error("verfy failure ver_code= ", short_message.phoneCode)
        end
    else
        log.error("args error!")
    end
end

--测试短信：其实只需要host和url就可以http.get成功。注意先将host域名转成ip
local function TestSms()
    --local dnsHost = "http://sms.shlianlu.com"  --端口就是默认的80 
    local host = "39.107.105.7"     --上面的connect失败。需要 转成ip
    local jsonUrl = "http://sms.shlianlu.com/sms.aspx?dataType=json&action=overage&userid=18839&account=zouzou2021&password=ZERO382237995"
    local xmlUrl = "http://sms.shlianlu.com/sms.aspx?action=overage&userid=18839&account=zouzou2021&password=ZERO382237995"
    local status, ret = httpc.get(host, xmlUrl)
    log.info("TestSmsstatus=", status)
    log.info("TestSms ret=", ret)
end


function short_message.Init()
    --db的key
    short_message.msgTemplate = {
        [1] = 'code_login',
        [2] = 'forget_secret',
        [3] = 'bind_phone',
        [4] = 'modify_first_step',
        [5] = 'modify_second_step',
        [6] = 'forget_safe_code',
    }
    short_message.phoneCode = {}
    --有效时间秒5分钟
    short_message.valideTime = 300    
    --
    short_message.hostList2 = {
        '115.182.203.188:8061',
    }
    short_message.hostListDns = {
        'http://sdk.entinfo.cn:8061',
        'http://sdk2.entinfo.cn:8061'
    }           

    short_message.url = '/webservice.asmx/mdsmssend'
    short_message.sn  = skynet.getenv('short_msg_sn') or 'SDK-666-010-02383'
    short_message.pwd = skynet.getenv('short_msg_pwd') or '258016'
    short_message.sign = skynet.getenv('short_msg_sign') or '伟艺云'
    short_message.rawContent = string.format('您本次验证码为:x 【%s】', SIGN)
    short_message.timeout = 100 * 25
end

skynet.start(function()
    skynet.dispatch("lua", function(session, address, cmd, ...)
        log.info("short_message dispatch cmd=", cmd)
        local f = short_message[cmd]
        if f then
            skynet.ret(skynet.pack(f(...)))
        else
            log.error(string.format("Unknown command %s", tostring(cmd)))
        end
    end)

    short_message.Init()
    skynet.register(".short_message")
end)
